﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;

namespace Accona.Remoting.Remoting
{
    public class ServiceLookup : IServiceLookup
    {
        private readonly Guid appId;
        private readonly IReceiver receiver;
        private readonly List<ServiceInfo> services = new List<ServiceInfo>();
        private bool running;

        public ServiceLookup(IReceiver receiver, Guid appId)
        {
            this.receiver = receiver;
            this.appId = appId;
            this.receiver.OnMessage += ReceiverOnMessage;
            var cleanerThread = new Thread(PerformClean);
            cleanerThread.IsBackground = true;
            cleanerThread.Name = "ServiceLookupCleaner";
            cleanerThread.Start();
        }

        #region IServiceLookup Members

        public IList<ServiceInfo> LookupServices(Type type)
        {
            lock (services)
            {
                return services.Where(a => a.SupportedInterface.Equals(type.FullName)).ToList();
            }
        }

        #endregion

        private void PerformClean()
        {
            running = true;
            while (running)
            {
                lock (services)
                {
                    int before = services.Count;
                    services.RemoveAll(a => (DateTime.Now - a.Timestamp).TotalSeconds > 2);
                    if (before != services.Count)
                        Console.WriteLine("{0} services discovered", services.Count);
                }
            }
        }

        private void ReceiverOnMessage(byte[] obj)
        {
            var stream = new MemoryStream(obj);
            var formatter = new BinaryFormatter();
            var announceMents = (IList<ServiceInfo>) formatter.Deserialize(stream);
            Guid serverId;
            if (announceMents.Count == 0)
                return;
            else
            {
                serverId = announceMents[0].ServerId;
                if (serverId == appId)
                    return; // sender is me
            }
            lock (services)
            {
                int before = services.Count;
                IEnumerable<ServiceInfo> filteredServices = from a in services
                                                            where !a.ServerId.Equals(serverId)
                                                            select a;
                services.Clear();
                services.AddRange(filteredServices);
                foreach (ServiceInfo announceMent in announceMents)
                {
                    announceMent.Timestamp = DateTime.Now;
                }
                if (announceMents[0].Action == ServiceInfo.AnnouncementAction.Add)
                    services.AddRange(announceMents);
                if (before != services.Count)
                    Console.WriteLine("{0} services discovered", services.Count);
            }
        }

        public void RegisterService(ServiceInfo info)
        {
            lock (services)
            {
                services.Add(info);
            }
        }

        public void RemoveService(ServiceInfo info)
        {
            lock (services)
            {
                IEnumerable<ServiceInfo> matchingServices =
                    services.Where(
                        a => a.ServerId.Equals(info.ServerId) && a.SupportedInterface.Equals(info.SupportedInterface));
                foreach (ServiceInfo serviceInfo in matchingServices)
                {
                    services.Remove(serviceInfo);
                }
            }
        }
    }
}